#include "vas_video.h"

namespace vas {

    void VideoInit() {
        if(VIDEO_PROCESSING == nullptr){
            VIDEO_PROCESSING = new std::map<std::string, std::pair<Channel, bool>>();
        }
    }


    void RegisterVideo(const Channel &channel) {

        if(VIDEO_PROCESSING == nullptr){
            throw VasRuntimeError("Video is not initialized, call VideoInit first;");
        }

        std::lock_guard<std::mutex> lk(VIDEO_NUM_MTX);
        if (VIDEO_PROCESSING->find(channel.channel) == VIDEO_PROCESSING->end()) {
            VIDEO_PROCESSING->insert({channel.channel, {channel, true}});
            std::stringstream msg;
            msg << channel.channel << " at " << channel.url << " is registered";
            VAS_INFO(msg.str());
            return;
        }
        if (VIDEO_PROCESSING->at(channel.channel).second) {
            std::stringstream msg;
            msg << channel.channel << " at " << channel.url << " is being handled";
            throw VasRuntimeError(msg.str());
        } else {
            std::stringstream msg;
            msg << channel.channel << " at " << channel.url << " is marked as stopped";
            throw VasRuntimeError(msg.str());
        }
    }

    void ReadVideo(const Channel &channel, void (*pf)(const cv::Mat &i, const Channel &c, long t)) {
        if(VIDEO_PROCESSING == nullptr){
            throw VasRuntimeError("Video is not initialized, call VideoInit first;");
        }
        if (VIDEO_PROCESSING->find(channel.channel) == VIDEO_PROCESSING->end()) {
            std::stringstream msg;
            msg << channel.channel << " at " << channel.url << " is not registered, pls register the video first";
            throw VasRuntimeError(msg.str());
        }
        cv::VideoCapture cap;
        try {
            cap.open(channel.url);
            if (cap.isOpened()) {
                for (;;) {
                    if (!VIDEO_PROCESSING->at(channel.channel).second) {
                        std::stringstream msg;
                        msg << channel.channel << " at " << channel.url << " is stopped";
                        VAS_INFO(msg.str());
                        break;
                    }
                    cv::Mat frame;
                    cap.read(frame);
                    if (frame.empty()) {
                        std::stringstream msg;
                        msg << channel.channel << " at " << channel.url << " read empty frame, stop reading";
                        VAS_INFO(msg.str());
                        break;
                    }
                    (*pf)(frame, channel, GetCurrentTime());
                }
                cap.release();
            } else {
                std::stringstream msg;
                msg << channel.channel << " at " << channel.url << " open failed";
                VAS_INFO(msg.str());
            }
        }
        catch (std::exception &e) {
            if (cap.isOpened()) {
                cap.release();
            }
            std::stringstream msg;
            msg << channel.channel << " at " << channel.url << " catche exception " << e.what();
            VAS_ERROR(msg.str());
        }
    }

    void StopVideo(const std::string &channel_id) {
        if(VIDEO_PROCESSING == nullptr){
            throw VasRuntimeError("Video is not initialized, call VideoInit first;");
        }
        std::lock_guard<std::mutex> lk(VIDEO_NUM_MTX);
        if (VIDEO_PROCESSING->find(channel_id) == VIDEO_PROCESSING->end()) {
            std::stringstream msg;
            msg << channel_id << " is not being handled";
            VAS_ERROR(msg.str());
            return;
        }
        if (VIDEO_PROCESSING->at(channel_id).second) {
            VIDEO_PROCESSING->at(channel_id).second = false;
            std::stringstream msg;
            msg << channel_id << " is marked as stopping";
            VAS_INFO(msg.str());
        } else {
            std::stringstream msg;
            msg << channel_id << " has been marked as stopping";
            VAS_ERROR(msg.str());
        }
    }

    std::vector<Channel> GetProcessingVideos() {
        if(VIDEO_PROCESSING == nullptr){
            throw VasRuntimeError("Video is not initialized, call VideoInit first;");
        }
        std::vector<Channel> channels;
        for (auto &i: *VIDEO_PROCESSING) {
            channels.emplace_back(i.second.first);
        }
        return channels;
    }

    void UnregisterVideo(const Channel &channel) {
        if(VIDEO_PROCESSING == nullptr){
            throw VasRuntimeError("Video is not initialized, call VideoInit first;");
        }
        std::lock_guard<std::mutex> lk(VIDEO_NUM_MTX);
        auto i = VIDEO_PROCESSING->find(channel.channel);
        if (i != VIDEO_PROCESSING->end()) {
            VIDEO_PROCESSING->erase(i);
            std::stringstream msg;
            msg << channel.channel << " at " << channel.url << " is unregistered";
            VAS_INFO(msg.str());
        } else {
            std::stringstream msg;
            msg << channel.channel << " at " << channel.url << " has been unregistered";
            VAS_ERROR(msg.str());
        }
    }

    int GetProcessingVideoNum() {
        if(VIDEO_PROCESSING == nullptr){
            throw VasRuntimeError("Video is not initialized, call VideoInit first;");
        }
        std::lock_guard<std::mutex> lk(VIDEO_NUM_MTX);
        return VIDEO_PROCESSING->size();
    }




}
